package com.tianji.learning.utils;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2025/6/30 15:29
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class LearningRecordDelayTaskHandler implements ApplicationContextAware, CommandLineRunner, EnvironmentAware, InitializingBean {
    private final StringRedisTemplate redisTemplate;
    private final String KEY_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<DelayTaskData>> delayQueue = new DelayQueue();
    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;
    private static Boolean flag = true;

    @PostConstruct
    public void init() {
        log.info("Bean初始化阶段执行");
        // new Thread(() -> handleDelayTask()).start();
        CompletableFuture.runAsync(this::handleDelayTask);
    }

    @PreDestroy
    public void destory(){
        flag = false;
    }

    /**
     * 处理延时检测任务
     */
    public void handleDelayTask() {
        log.info("开始处理播放进度延时检测任务逻辑...");
        while (flag) {
            try {
                // 1、从延时队列中尝试 take 获取延时检测任务
                DelayTask<DelayTaskData> delayTask = delayQueue.take();
                DelayTaskData data = delayTask.getData();
                log.info("获取到延时检测任务，任务数据：{}", data);

                // 2、从redis中查询实时的播放进度
                LearningRecord redisRecord = readLearningRecordFromCache(data.getLessonId(), data.getSectionId());

                // 3、比较延时任务重的本地进度 与 redis中的实时进度 是否一致
                // 3.1、如果不一致，证明用户还在观看，直接跳过
                if (ObjectUtil.isEmpty(redisRecord) || ObjectUtil.notEqual(data.getMoment(), redisRecord.getMoment())) {
                    continue;
                }

                // 3.2、如果一致，则说明用户已经停止观看，需要将redis中的实时进度 更新到学习记录表中，并更新 课表 数据
                // 3.2.1、更新学习记录表（主要是更新 播放进度 moment）
                redisRecord.setFinished(null);
                recordMapper.updateById(redisRecord);

                // 3.2.2、更新课表数据（更新 最近学习小节ID、最近学习时间）
                LearningLesson lesson = new LearningLesson();
                lesson.setId(data.getLessonId());
                lesson.setLatestSectionId(data.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(lesson);
            } catch (Exception e) {
                log.error("处理某个播放进度延时检测任务逻辑异常:{}", e);
            }
        }
    }

    /**
     * 1、从缓存中读取学习记录
     *
     * @param lessonId  课表ID
     * @param sectionId 小节ID
     * @return
     */
    public LearningRecord readLearningRecordFromCache(Long lessonId, Long sectionId) {
        log.info("从缓存中读取学习记录，课表ID：{}，小节ID：{}", lessonId, sectionId);
        // 1、组装大key learning:record:{}
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);

        // 2、组装hashkey
        String hashKey = sectionId.toString();

        // 3、发起查询，得到hashvalue
        String hashValue = (String) redisTemplate.opsForHash().get(key, hashKey);
        if (ObjectUtil.isEmpty(hashValue)) {
            log.warn("缓存中没有查询到学习记录，课表ID：{}，小节ID：{}", lessonId, sectionId);
            return null;
        }

        // 4、将 hashvalue 转为 LearningRecord
        LearningRecord learningRecord = JsonUtils.toBean(hashValue, LearningRecord.class);
        return learningRecord;
    }

    /**
     * 2、将学习记录写入缓存
     *
     * @param learningRecord 学习记录
     */
    public void writeLearningRecordToCache(LearningRecord learningRecord) {
        log.info("将学习记录写入缓存，学习记录数据：{}", learningRecord);
        // 1、组装大key learning:record:{}
        String key = StringUtils.format(KEY_TEMPLATE, learningRecord.getLessonId());

        // 2、组装hashkey
        String hashKey = learningRecord.getSectionId().toString();

        // 3、组装hashvalue
        String hashValue = JsonUtils.toJsonStr(BeanUtils.toBean(learningRecord, RedisRecordData.class));

        // 4、写入缓存
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        redisTemplate.expire(key, Duration.ofMinutes(1));
    }

    /**
     * 3、实时更新学习进度 & 添加学习记录延时检测任务
     *
     * @param learningRecord
     */
    public void addLearningRecordDelayCheckTask(LearningRecord learningRecord) {
        log.info("实时更新学习进度 & 提交延时检测任务，学习记录数据：{}", learningRecord);
        // 1、暂存播放记录到redis
        writeLearningRecordToCache(learningRecord);

        // 2、提交延时检测任务
        delayQueue.add(DelayTask.of(Duration.ofSeconds(20), BeanUtils.toBean(learningRecord, DelayTaskData.class)));
    }

    /**
     * 4、清除学习记录缓存
     *
     * @param lessonId  课表ID
     * @param sectionId 小节ID
     */
    public void cleanLearningRecordCache(Long lessonId, Long sectionId) {
        log.info("清除学习记录缓存，课表ID：{}，小节ID：{}", lessonId, sectionId);
        // 1、组装大key learning:record:{}
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);

        // 2、组装hashkey
        String hashKey = sectionId.toString();

        // 3、删除缓存
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    /**
     * 5、根据课表ID 获取最新学习记录（进度）
     * @param lessonId 课表ID
     * @return
     */
    public Map<Long,LearningRecord> getLearningRecord4LessonId(Long lessonId){
        log.info("根据课表ID获取 实时学习记录，课表ID：{}", lessonId);
        // 1、组装大key learning:record:{}
        String key = StringUtils.format(KEY_TEMPLATE, lessonId);

        // 2、获取key对应的所有学习记录
        Map<Long,LearningRecord> learningReordMap = new HashMap<>();
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(key);
        entries.forEach((k,v) -> {
            String hashkey = (String) k;
            String hashvalueJson = (String) v;
            LearningRecord learningRecord = JsonUtils.toBean(hashvalueJson, LearningRecord.class);
            learningReordMap.put(Long.valueOf(hashkey),learningRecord);
        });

        return learningReordMap;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("setApplicationContext....");
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println("run.....");
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("setEnvironment.....");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet.....");
    }


    @Data
    class RedisRecordData {
        /**
         * 学习记录的id
         */
        private Long id;

        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;

        /**
         * 是否完成学习，默认false
         */
        private Boolean finished;
    }

    @Data
    class DelayTaskData {
        /**
         * 课表ID
         */
        private Long lessonId;
        /**
         * 小节ID
         */
        private Long sectionId;
        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;
    }
}
